/*
 * Copyright 2010 The MIauto Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include <stdio.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/delay.h>
#include "xbee.h"
#include "usart.h"

PacketData packetReceive;

/**
 * SIGNAL SIG_UART_RECV
 *
 * On data in buffer interrupt
 */
SIGNAL (SIG_UART_RECV)
{
	uint8_t newChar;
	newChar = USART_Receive();

	if(XBee_ComposePacket(newChar ) == TRUE){
		/* remove the interrupt */
		UCSRB &= ~(1 << RXCIE);
	}
}

/**
 * getReceivePacket
 */
PacketData* XBee_GetReceivePacket(){
	return &packetReceive;
}

void XBee_ResetPacket(PacketData* packet)
{
	packet->dataPtr = (uint8_t*) packet;
	packet->crc = 0;
	packet->rxState = XBEE_PACKET_RX_START;
	packet->length = 0;
	packet->index = 0;
	packet->apiId = 0;
	packet->escape = FALSE;
	packet->finished = FALSE;
	memset(packet->payload, 0, 100);
	UCSRB |= (1 << RXCIE);
}

bool XBee_ComposePacket(uint8_t newChar)
{
	switch (packetReceive.rxState)
	{
	/* Receive the start packet */
	case XBEE_PACKET_RX_START:
		if (newChar == XBEE_PACKET_STARTBYTE)
		{
			packetReceive.rxState = XBEE_PACKET_RX_LENGTH_1;
		}
		break;
	case XBEE_PACKET_RX_LENGTH_1:
		if (packetReceive.escape == TRUE)
		{
			/* reset the trigger */
			packetReceive.escape = FALSE;
			/* escape the byte */
			newChar ^= 0x20;
		}
		else if (newChar == 0x7D)
		{
			/*set the trigger*/
			packetReceive.escape = TRUE;
			return FALSE;
		}

		packetReceive.length = newChar;
		packetReceive.length <<= 8;
		packetReceive.rxState = XBEE_PACKET_RX_LENGTH_2;
		break;
	case XBEE_PACKET_RX_LENGTH_2:
		if (packetReceive.escape == TRUE)
		{
			/* reset the trigger */
			packetReceive.escape = FALSE;
			/* escape the byte */
			newChar ^= 0x20;
		}
		else if (newChar == 0x7D)
		{
			/*set the trigger*/
			packetReceive.escape = TRUE;
			return FALSE;
		}
		packetReceive.length += newChar;

		if (packetReceive.length > XBEE_MAX_PACKET_SIZE)
		{
			packetReceive.rxState = XBEE_PACKET_RX_START;

		}
		else
			packetReceive.rxState = XBEE_PACKET_RX_PAYLOAD;

		packetReceive.crc = 0;
		break;
	case XBEE_PACKET_RX_PAYLOAD:
		if (packetReceive.escape == TRUE)
		{
			/* reset the trigger */
			packetReceive.escape = FALSE;
			/* escape the byte */
			newChar ^= 0x20;
		}
		else if (newChar == 0x7D)
		{
			/*set the trigger*/
			packetReceive.escape = TRUE;
			return FALSE;
		}

		packetReceive.crc += newChar;

		*packetReceive.dataPtr++ = newChar;
		if (++packetReceive.index >= packetReceive.length)
		{
			packetReceive.rxState = XBEE_PACKET_RX_CRC;
		}

		break;
	case XBEE_PACKET_RX_CRC:
		if (packetReceive.escape == TRUE)
		{
			/* reset the trigger */
			packetReceive.escape = FALSE;
			/* escape the byte */
			newChar ^= 0x20;

		}else if (newChar == 0x7D)
		{
			/*set the trigger*/
			packetReceive.escape = TRUE;

			return FALSE;
		}

		packetReceive.rxState = XBEE_PACKET_RX_START;
		packetReceive.crc += newChar;
		if ((packetReceive.crc) == 0xFF)
		{
			packetReceive.finished = TRUE;
			return TRUE;
		}
		else
		{
			packetReceive.finished = FALSE;
			XBee_ResetPacket(&packetReceive);

			return FALSE;
		}
	default:
		break;
	}

	return FALSE;
}

void XBee_SendPacket(PacketData* packet, int16_t datalength)
{

	USART_Transmit(XBEE_PACKET_STARTBYTE);
	int size = datalength;
	switch (packet->apiId)
	{
	case XBEE_TX64:
		size += 11;
		break;
	case XBEE_TX16:
		size += 5;
		break;
	case XBEE_ATCOMMAND:
		size = (datalength > 0) ? 8 : 4;
		break;
	default:
		size = 0;
		break;
	}
	USART_Transmit((size >> 8) & 0xFF);
	USART_Transmit(size & 0xFF);
	packet->crc = 0;
	uint8_t* p = (uint8_t*) packet;
	while (size--)
	{
		USART_Transmit(*p);
		packet->crc += *p++;
	}

	USART_Transmit(0xFF - packet->crc);
}

bool XBee_ReadRX64Packet(PacketData* xbp, uint64* srcAddress64,
		uint16_t* srcAddress16, uint8_t* options, uint8_t** data, uint8_t* datalength)
{
	if (xbp->apiId != XBEE_RX64)
		return FALSE;

	uint8_t i;
	if (srcAddress64)
	{
		for (i = 0; i < 8; i++)
		{
			*srcAddress64 <<= i * 8;
			*srcAddress64 += xbp->rx64.source64[i];
		}
	}

	if (options)
		*options = xbp->rx64.options;
	if (data)
		*data = xbp->rx64.data;
	if (datalength)
		*datalength = xbp->length - 12;

	return TRUE;
}

